/*=========================================================================

 Program:   Visualization Toolkit
 Module:    vtkStructuredAMRNeighbor.cxx

 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 All rights reserved.
 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notice for more information.

 =========================================================================*/
#include "vtkStructuredAMRNeighbor.h"
#include "vtkStructuredExtent.h"

vtkStructuredAMRNeighbor::vtkStructuredAMRNeighbor()
{
  this->GridLevel = -1;
  this->NeighborLevel = -1;
  this->NeighborID = 0;
  this->RelationShip = vtkStructuredAMRNeighbor::UNDEFINED;

  for (int i = 0; i < 3; ++i)
  {
    this->Orientation[i] = vtkStructuredNeighbor::UNDEFINED;
    int minIdx = i * 2;
    int maxIdx = i * 2 + 1;
    this->GridOverlapExtent[minIdx] = this->GridOverlapExtent[maxIdx] =
      this->OverlapExtent[minIdx] = this->OverlapExtent[maxIdx] = this->SendExtent[minIdx] =
        this->SendExtent[maxIdx] = this->RcvExtent[minIdx] = this->RcvExtent[maxIdx] = -1;
  } // END for all dimensions
}

//------------------------------------------------------------------------------
vtkStructuredAMRNeighbor::vtkStructuredAMRNeighbor(const int gridLevel, const int neiID,
  const int neighborLevel, int gridOverlap[6], int neiOverlap[6], int orient[3],
  const int relationShip)
{
  this->GridLevel = gridLevel;
  this->NeighborID = neiID;
  this->NeighborLevel = neighborLevel;
  this->RelationShip = relationShip;

  for (int i = 0; i < 3; ++i)
  {
    int minIdx = i * 2;
    int maxIdx = i * 2 + 1;

    this->RcvExtent[minIdx] = this->OverlapExtent[minIdx] = neiOverlap[minIdx];
    this->RcvExtent[maxIdx] = this->OverlapExtent[maxIdx] = neiOverlap[maxIdx];

    this->SendExtent[minIdx] = this->GridOverlapExtent[minIdx] = gridOverlap[minIdx];
    this->SendExtent[maxIdx] = this->GridOverlapExtent[maxIdx] = gridOverlap[maxIdx];

    this->Orientation[i] = orient[i];
  } // END for all dimensions
}

//------------------------------------------------------------------------------
vtkStructuredAMRNeighbor& vtkStructuredAMRNeighbor::operator=(const vtkStructuredAMRNeighbor& N)
{
  if (this != &N)
  {
    this->GridLevel = N.GridLevel;
    this->NeighborID = N.NeighborID;
    this->NeighborLevel = N.NeighborLevel;
    this->RelationShip = N.RelationShip;

    for (int i = 0; i < 3; ++i)
    {
      int minIdx = i * 2;
      int maxIdx = i * 2 + 1;

      this->RcvExtent[minIdx] = N.RcvExtent[minIdx];
      this->RcvExtent[maxIdx] = N.RcvExtent[maxIdx];

      this->SendExtent[minIdx] = N.SendExtent[minIdx];
      this->SendExtent[maxIdx] = N.SendExtent[maxIdx];

      this->OverlapExtent[minIdx] = N.OverlapExtent[minIdx];
      this->OverlapExtent[maxIdx] = N.OverlapExtent[maxIdx];

      this->GridOverlapExtent[minIdx] = N.GridOverlapExtent[minIdx];
      this->GridOverlapExtent[maxIdx] = N.GridOverlapExtent[maxIdx];
    } // END for all dimensions
  }   // END if
  return *this;
}

//------------------------------------------------------------------------------
std::string vtkStructuredAMRNeighbor::GetRelationShipString()
{
  std::string str;
  switch (this->RelationShip)
  {
    case PARENT:
      str = "PARENT";
      break;
    case PARTIALLY_OVERLAPPING_PARENT:
      str = "PARTIALLY_OVERLAPPING_PARENT";
      break;
    case CHILD:
      str = "CHILD";
      break;
    case PARTIALLY_OVERLAPPING_CHILD:
      str = "PARTIALLY_OVERLAPPING_CHILD";
      break;
    case SAME_LEVEL_SIBLING:
      str = "SAME_LEVEL_SIBLING";
      break;
    case COARSE_TO_FINE_SIBLING:
      str = "COARSE_TO_FINE_SIBLING";
      break;
    case FINE_TO_COARSE_SIBLING:
      str = "FINE_TO_COARSE_SIBLING";
      break;
    case UNDEFINED: /* intentional fall-through */
    default:
      str = "UNDEFINED";
  } // END switch

  return (str);
}

//------------------------------------------------------------------------------
void vtkStructuredAMRNeighbor::GetReceiveExtentOnGrid(const int ng, int gridExtent[6], int ext[6])
{
  for (int i = 0; i < 6; ++i)
  {
    ext[i] = this->GridOverlapExtent[i];
  }

  for (int i = 0; i < 3; ++i)
  {
    switch (this->Orientation[i])
    {
      case vtkStructuredNeighbor::SUPERSET:
        /* NO OP */
        break;
      case vtkStructuredNeighbor::SUBSET_HI:
      case vtkStructuredNeighbor::HI:
        ext[i * 2 + 1] += ng;
        break;
      case vtkStructuredNeighbor::SUBSET_LO:
      case vtkStructuredNeighbor::LO:
        ext[i * 2] -= ng;
        break;
      case vtkStructuredNeighbor::SUBSET_BOTH:
        ext[i * 2] -= ng;
        ext[i * 2 + 1] += ng;
        break;
      default:; /* NO OP */
    }           // END switch
  }             // END for all dimensions
  vtkStructuredExtent::Clamp(ext, gridExtent);
}

//------------------------------------------------------------------------------
void vtkStructuredAMRNeighbor::ComputeSendAndReceiveExtent(int gridRealExtent[6],
  int* vtkNotUsed(gridGhostedExtent), int neiRealExtent[6], int* vtkNotUsed(WholeExtent),
  const int N)
{

  // TODO: Here we need to make sure that the send/rcv extent between a coarse
  // fine boundary will be as such that ghost layers of the fine grid will cover
  // the entire lower res cell based on the level difference between the
  // grid and its neighbor.

  for (int i = 0; i < 3; ++i)
  {
    switch (this->Orientation[i])
    {
      case vtkStructuredNeighbor::SUPERSET:
        this->SendExtent[i * 2] -= N;
        this->SendExtent[i * 2 + 1] += N;
        break;
      case vtkStructuredNeighbor::SUBSET_HI:
      case vtkStructuredNeighbor::HI:
        this->RcvExtent[i * 2 + 1] += N;
        this->SendExtent[i * 2] -= N;
        break;
      case vtkStructuredNeighbor::SUBSET_LO:
      case vtkStructuredNeighbor::LO:
        this->RcvExtent[i * 2] -= N;
        this->SendExtent[i * 2 + 1] += N;
        break;
      case vtkStructuredNeighbor::SUBSET_BOTH:
        this->RcvExtent[i * 2] -= N;
        this->SendExtent[i * 2 + 1] += N;
        this->RcvExtent[i * 2 + 1] += N;
        this->SendExtent[i * 2] -= N;
        break;
      default:; /* NO OP */
    }           // END switch
  }             // END for all dimensions

  // Hmm...restricting receive extent to the real extent of the neighbor
  vtkStructuredExtent::Clamp(this->RcvExtent, neiRealExtent);
  vtkStructuredExtent::Clamp(this->SendExtent, gridRealExtent);
}
